## @package karboom.aggregate
# Layer aggregator
#
# Encapsulates layers for use with the renderer.
#

from karboom.classes import Renderable

try:
    import curses
except:
    pass

import time, datetime
import cairo

## Aggregate
#
# This class is used to collate layers. Layers are attached to this class and rendered.
#
class Aggregate(Renderable):
    
    ## List of layers attached to the aggregate
    layers = []
    
    ## Cairo surface
    surface = None
    
    def __init__(self, **kwargs):
        Renderable.__init__(self, **kwargs)
        
        self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.size[0], self.size[1])
    
    ## Aggregate render
    #
    # Render a single frame of the aggregate to the surface.
    # @param frame: Frame
    #
    def __render__(self, frame):
        for l in sorted(self.layers, key = lambda x: x.level):
            if frame >= (l.starttime * self.fps) and frame <= (l.endtime * self.fps):
                l.context, l.size, l.fps, l.frame = cairo.Context(self.surface), self.size, self.fps, frame
                l.__render__()
                del l.context, l.size, l.fps, l.frame
    
    ## Render frame to PNG
    #
    # This function renders the contents of one frame in the aggregate to a PNG file.
    #
    def render_frame_to_png(self, file, frame = 0):
        self.__render__(frame)
        self.surface.write_to_png(file)
    
    ## Render to rawvideo
    #
    # This function renders the entire aggregate to an ARGB32 raw video.
    # 
    def render_to_rawvideo(self, file, startframe = 0, endframe = 0):
        fo = open(file, 'wb')
        
        starttime = time.time()
        
        for f in xrange(startframe, endframe):
            tdelta = time.time() - starttime
            fps = (f - startframe)/tdelta if (f - startframe) > 0 else 0
            
            msg = 'Rendering frame: %d/%d (time elapsed: %s, %sFPS: %.2f)' % (
                                                                              f - startframe + 1,
                                                                              endframe - startframe,
                                                                              datetime.timedelta(seconds=int(tdelta)),
                                                                              'estimated time remaining: %s, ' % datetime.timedelta(seconds=int((endframe - startframe)/fps - tdelta)) if (f - startframe) > 20 else '',
                                                                              fps
                                                                              )
            try:
                curses.setupterm()
                print curses.tigetstr('el') + msg + curses.tigetstr('cuu1')
            except:
                print msg
            cr = cairo.Context(self.surface)
            cr.set_source_rgba(0, 0, 0, 0)
            cr.set_operator(cairo.OPERATOR_SOURCE)
            cr.paint()
            self.__render__(f)
            fo.write(self.surface.get_data())
        
        endtime = time.time()
        
        msg = 'Rendered %d frames. (time elapsed: %s, average FPS: %.2f)' % (endframe - startframe, datetime.timedelta(seconds=int(endtime - starttime)), (endframe - startframe)/(endtime - starttime))
        
        try:
            curses.setupterm()
            print curses.tigetstr('el') + msg
        except:
            print msg
        
        fo.close()